﻿/// <reference path="_namespace.js" />
/// <reference path="/ajax/components/Component.js" />
/// <reference path="/ajax/DomElements.js" />
/// <reference path="/ajax/events/EventRouter.js" />

/*****************************************************************************
Ajax library class definition. Kevin Robinson 2006.
Class: ajax.components.Container
/*****************************************************************************/

ajax.lang.addNamespace("ajax.components");

//-----------------------------------------------------------------------------
ajax.components.Container = function(doc, parentNode, layerName, w, h) // Constructor
{   ///<field name="layerStyle" type="domStyle"/>
    ajax.components.Container.superConstructor.apply(this, arguments);        
    this.parentNode         = (parentNode) ? parentNode : this.doc.body;
    // Enforce an ID for every container.
    this.layerName          = layerName ? layerName : $new("ajax.math.Guid").toString(); 
    this.width              = w;
    this.height             = h;
    this.x                  = 0;
    this.y                  = 0;
    this.layer              = null;  
    this._isTemplateControl = false;    // When set to true, the Object.getConstructorName(this) is used to determine the Url 
                                        //of the template to use (with .html appended). This means that each Template
                                        // control can only automatically load one template. 
    this.units          = 
    {
        size: {width: "px", height: "px"},
        position: {x: "px", y: "px"}
    };
    this.minWidth           = 0;
	this.minHeight          = 0;
	
    
    this.resourceCache  = ajax.lang.safeNew("ajax.lang.ResourceCache");
    this.layerOffsetX   = 0;
    this.layerOffsetY   = 0;
    
    // Styles
    this.layerStyle =
    {
        position: "relative",
        overflow: "auto",
        visibility: "hidden"
    };
    
    // Public Events
    this.getEvent(this.window, "onLoad");
    this.getEvent(this.window, "onMove");
    this.getEvent(this.window, "onShow");
    this.getEvent(this.window, "onDisposing");
    this.getEvent(this.window, "onDispose");
    this.getEvent(this.window, "onHide");      
    this.getEvent(this.window, "onClose");  
    this.getEvent(this.window, "onResize");  
    // Global Events
    ajax.lang.getEvent(this.window, ajax.components, "onContainerClick");
}

//-----------------------------------------------------------------------------
ajax.lang.extend("ajax.components.Container", "ajax.components.Component"); // Inherits
if ($getDesignMode())
    ajax.components.Container.prototype = ajax.components.Component.prototype;

ajax.components.Container.prototype.visible             = false;
ajax.components.Container.prototype.width               = 0;
ajax.components.Container.prototype.height              = 0;
ajax.components.Container.prototype.x                   = 0;
ajax.components.Container.prototype.y                   = 0;
ajax.components.Container.prototype._isTemplateControl  = false;
ajax.components.Container.prototype.units               = null;
ajax.components.Container.prototype.minWidth            = 0;
ajax.components.Container.prototype.minHeight           = 0;
ajax.components.Container.prototype.resourceCache       = null;
ajax.components.Container.prototype.layerOffsetX        = 0;
ajax.components.Container.prototype.layerOffsetY        = 0
ajax.components.Container.prototype.layerStyle          = {}; // domStyle
ajax.components.Container.prototype.add_load          = function(handler) { this.getEvent(this.getWindow(), "onLoad").addListener(handler); }
ajax.components.Container.prototype.remove_load       = function(handler) { this.getEvent(this.getWindow(), "onLoad").removeListener(handler); }
ajax.components.Container.prototype.add_move          = function(handler) { this.getEvent(this.getWindow(), "onMove").addListener(handler); }
ajax.components.Container.prototype.remove_move       = function(handler) { this.getEvent(this.getWindow(), "onMove").removeListener(handler); }
ajax.components.Container.prototype.add_show          = function(handler) { this.getEvent(this.getWindow(), "onShow").addListener(handler); }
ajax.components.Container.prototype.remove_show       = function(handler) { this.getEvent(this.getWindow(), "onShow").removeListener(handler); }
ajax.components.Container.prototype.add_disposing     = function(handler) { this.getEvent(this.getWindow(), "onDisposing").addListener(handler); }
ajax.components.Container.prototype.remove_disposing  = function(handler) { this.getEvent(this.getWindow(), "onDisposing").removeListener(handler); }
ajax.components.Container.prototype.add_dispose       = function(handler) { this.getEvent(this.getWindow(), "onDispose").addListener(handler); }
ajax.components.Container.prototype.remove_dispose    = function(handler) { this.getEvent(this.getWindow(), "onDispose").removeListener(handler); }
ajax.components.Container.prototype.add_hide          = function(handler) { this.getEvent(this.getWindow(), "onHide").addListener(handler); }
ajax.components.Container.prototype.remove_hide       = function(handler) { this.getEvent(this.getWindow(), "onHide").removeListener(handler); }
ajax.components.Container.prototype.add_close         = function(handler) { this.getEvent(this.getWindow(), "onClose").addListener(handler); }
ajax.components.Container.prototype.remove_close      = function(handler) { this.getEvent(this.getWindow(), "onClose").removeListener(handler); }
ajax.components.Container.prototype.add_resize        = function(handler) { this.getEvent(this.getWindow(), "onResize").addListener(handler); }
ajax.components.Container.prototype.remove_resize     = function(handler) { this.getEvent(this.getWindow(), "onResize").removeListener(handler); }
ajax.components.Container.prototype.getLayer            = function()
{///<returns type="domElement"/>
    return this.layer;
}
ajax.components.Container.prototype.getParentNode = function()
{///<returns type="domElement"/>
    return this.parentNode;
}
ajax.components.Container.prototype.getLayerStyle = function()
{///<returns type="domStyle"/>
    return this.layerStyle;
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.get_isTemplateControl = function() { return this._isTemplateControl; }
ajax.components.Container.prototype.set_isTemplateControl = function(value) 
{///<summary>Setting to true means you have supplied an html file stored next to this class with a filename of CLASSNAME.js.html. 
/// This file will be auto-loaded.
/// Ajaxna will pick out all DOM elements with an "id" attribute, strip out "TEMPLATE_" and set the remainder as a property of this class instance.
/// and then set the id of the DOM element to a blank string. Eg, a textbox would look like this:
/// ...input id="TEMPLATE__txtName"...
/// Notice the use of a DOUBLE underscore, meaning that the dom element is stored as a "private" member of the class instance.
/// </summary>
    this._isTemplateControl = value;
};

ajax.components.Container.prototype.get_isLoaded = function()
{///<returns type="boolean">True if this container is loaded.</returns>
    return (this.getLayer() != null);
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.loadAutoTemplate = function(element)
{   ///<summary>Will use this instances type name to load the template html content into the element supplied and expose its elements (anything with an ID)</summary>
    ///<param name="ele" type="domElement"/>
    var templateSrc = ajax.lang.getClassPath(Object.getConstructorName(this)) + ".html";    
    var template = this.resourceCache.getFile(templateSrc);

    var loadingDiv = this.loadContentToDiv(template);

    var processNode = function(thisNode)
    {
        if (thisNode.id)
        {
            // Store handle to the node in this container.
            var eleID = thisNode.id;
            eleID = eleID.replace("TEMPLATE_", ""); // This procedure can be followed            
            this[eleID] = thisNode;
            thisNode.id = "";           // blank out id of element now we have a handle to it.
        }
        for (var i = 0; i < thisNode.childNodes.length; i++)
            processNode.call(this, thisNode.childNodes[i]);
    }
    processNode.call(this, loadingDiv);

    // Move to visible location and remove loader div
    for (var i = 0; i < loadingDiv.childNodes.length; i++)
        element.appendChild(loadingDiv.childNodes[i]);
    this.removeNode(loadingDiv);
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.load = function()
{
    // Create this now, in its hidden state
    this.create();
    this.setSize(this.width, this.height);

    // AutoLoad template?
    if (this.get_isTemplateControl())
        this.loadAutoTemplate(this.getLayer());

    this.onLoad.call(this);
}



//-----------------------------------------------------------------------------
ajax.components.Container.prototype.addHandlers = function()
{
    // As long as this base is called, then anytime a click occures on a window (or container), the global event will fire.
    ajax.lang.getEvent(this.window, this.layer, "onclick").addListener(function(e)
    {
        ajax.components.onContainerClick.call(this, { raw: e, sender: this });
    } .bind(this)
    );
}



//-----------------------------------------------------------------------------	
ajax.components.Container.prototype.create = function()
{
    ajax.lang.safeDestroy(this.doc.getElementById(this.layerName));
    this.layer = this.doc.createElement("div");
    this.layer.id = this.layerName;
    this.copyProperties(this.layer.style, this.layerStyle);
    this.parentNode.appendChild(this.layer);    
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.getVisible = function()
{///<returns type="boolean">Returns true if this object is visible.</returns>
    return this.visible;
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.setVisible = function(val)
{///<summary>Pass true to show this object, false to hide it.</summary>
    if (val)
        this.show();
    else
        this.hide();
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.getSize = function()
{///<returns type="ajax.geometry.shapes.Rectangle">Returns the outer size of this object.</returns>        
    return $new("ajax.geometry.shapes.Rectangle", 0, 0, this.width, this.height);    
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.setSize = function(width, height)
{///<summary>Sets the outer size of this object.</summary>
    // Final validation
    width = Math.max(width, this.minWidth);
    height = Math.max(height, this.minHeight);

    var w = width.toString() + this.units.size.width;
    var h = height.toString() + this.units.size.height;
    this.layer.style.width = w;
    this.layer.style.height = h;

    // If the size has changed then store the new values and fire the onResize event
    if (width != this.width || height != this.height)
    {
        this.width = width;
        this.height = height;
        // Fire our onResize event...
        this.onResize.call(this, this.getRect());
    }
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.setSizeToContainer = function()
{
    this.setSizeToContainerWidth();
    this.setSizeToContainerHeight();
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.setSizeToContainerWidth = function()
{
    var w = this.getDomNodeSize(this.parentNode).width;
    this.setSize(w, this.height);
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.setSizeToContainerHeight = function()
{
    var h = this.getDomNodeSize(this.parentNode).height;
    this.setSize(this.width, h);
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.getDomNodeSize = function(node)
{
    var w = (this.doc.body == node) ? this.doc.documentElement.clientWidth : node.offsetWidth;
    var h = (this.doc.body == node) ? this.doc.documentElement.clientHeight : node.offsetHeight;
    return { width: w, height: h };
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.getRect = function()
{
    return ajax.lang.safeNew("ajax.geometry.shapes.Rectangle", this.x, this.y, this.width, this.height);
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.getPosition = function()
{
    return { x: this.x, y: this.y };
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.setPosition = function(x, y, parentEle, e)
{///<param name="parentEle" type="domElement" optional="true"/>
 ///<param name="e" type="window.event" optional="true"/>
    if (e)
    {
        // Set pos to be the location of the event in relation to the parent Node    		    
        var offset = this.getEventLayerOffset(e, this.parentNode, true);
        x = offset.x;
        y = offset.y;
    }
    else if (parentEle)
    {
        /*debugger;
        We want to set our position relative to another DOM node.
        So we get the offset of that DOM node from our parentNode.	
        */
        var elePos = this.getElementLayerOffset(parentEle, this.parentNode);
        x = elePos.x;
        y = elePos.y + parentEle.offsetHeight;
    }
    else if (typeof (x) != "number" || typeof (y) != "number")
    {
        // No movement then thanks
        x = this.x;
        y = this.y;
    }

    // Set the DOM ele
    if (this.layer)
    {
        this.layer.style.left = x + this.layerOffsetX + this.units.position.x;
        this.layer.style.top = y + this.layerOffsetY + this.units.position.y;
    }


    var oldX = this.x;
    var oldY = this.y;

    this.x = x;
    this.y = y;

    // If we have actually moved, fire onMove event, passing in our rectangle for position & dimensions.
    if (oldX != this.x || oldY != this.y)
        this.onMove.call(this, this.getRect());
}



//-----------------------------------------------------------------------------
ajax.components.Container.prototype.showBelow = function(parentEle)
{
    this.show(0, 0, parentEle, null);
    return false;
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.showEvent = function(event)
{
    var eventInfo = ajax.lang.safeNew("ajax.events.EventInfo", event);
    var e = eventInfo.raw;
    this.show(null, null, null, e);
    e.cancelBubble = true;
    return false;
};

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.showCentered = function()
{
    var parentSize = this.getDomNodeSize(this.parentNode);
    // Protect showing an element with the top off the screen
    var x = Math.max(parentSize.width / 2 - this.width / 2, 0);
    var y = Math.max(parentSize.height / 2 - this.height / 2, 0);
    this.show(x, y);
    return false;
};

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.showTopMiddle = function()
{
    var parentSize = this.getDomNodeSize(this.parentNode);
    this.show(parentSize.width / 2 - this.width / 2, 0);
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.showLeftMiddle = function()
{
    var parentSize = this.getDomNodeSize(this.parentNode);
    this.show(0, parentSize.height / 2 - this.height / 2);
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.show = function(x, y, parentEle, e)
{
    if (!this.layer)
        this.load();

    this.setPosition(x, y, parentEle, e);
    
    // Only set visibility if not already visible.
    if (!this.visible)
    {
        this.layer.style.visibility = "visible";
        this.visible = true;

        // fire onShow event
        this.onShow.call(this, this.getRect());
    }

    return false;
}




//-----------------------------------------------------------------------------
ajax.components.Container.prototype.hide = function()
{
    if (!this.layer)
        return;

    this.visible = false;
    this.layer.style.visibility = "hidden";
    this.layer.style.left = "0" + this.units.position.x;
    this.layer.style.top = "0" + this.units.position.y;

    this.onHide.call(this);
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.close = function()
{
    this.hide();
    this.onClose.call(this);
    this.dispose();
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.dispose = function()
{
    this.onDisposing.call(this);

    // delete the layer if still exists
    if (this.layer && this.layer.parentNode)
        this.removeNode(this.layer);

    this.layer = null;

    // Fire onDispose event    
    this.onDispose.call(this);
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.noSelectHandler = function(e) // Stops Ie select text
{
    // is this coming frm a valid control to select from?
    var ele = e.srcElement || e.target;

    if (ele && (ele.isTextEdit || ele.isSelectable))
    {
        e.returnValue = true;
        return true;
    }
    else
    {
        e.returnValue = false;
        return false;
    }
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.toString = function()
{
    return "ajax.components.Container";
}

//-----------------------------------------------------------------------------
ajax.components.Container.prototype.toClassName = function()
{
    return ajax.lang.getClassName("ajax.components.Container");    
}

//-----------------------------------------------------------------------------
ajax.components.Container.cast = function(obj)
{///<returns type="ajax.components.Container"/>
    return obj;
}